/*
 * @Author: wangcb
 * @Email: wangcbgg@163.com
 * @Date: 2021-10-21 15:56:57
 * @LastEditTime: 2021-11-17 11:06:28
 * 常用工具方法
 */
/** Cookie */
export const Cookie = {
    setItem(key, value) {
        document.cookie = key + '=' + value + '; path=/';
    },
    getItem(key) {
        const values = document.cookie.replace(/\s/g, '').split(';');
        for (let i = 0, len = values.length; i < len; i++) {
            const item = values[i];
            const kv = item.split('=');
            if (kv[0] === key)
                return kv[1];
        }
        return null;
    },
    removeItem(key) {
        document.cookie = key + '=; max-age=0; path=/';
    },
    clear() {
        const values = document.cookie.replace(/\s/g, '').split(';');
        for (let i = 0, len = values.length; i < len; i++) {
            const item = values[i];
            const kv = item.split('=');
            this.removeItem(kv[0]);
        }
    }
};
/**
 * 公用存储类
 * @namespace Storage
 */
export const Storage = {
    /**
     * 设置存储键值
     * @param {string} key 键
     * @param {any} value 值
     */
    setItem(key, value) {
        if (sessionStorage) {
            sessionStorage.setItem(key, value);
        }
        else {
            Cookie.setItem(key, value);
        }
    },
    /**
     * 获取存储值
     * @param {string} key 键
     * @returns {string | null} key对应的值
     */
    getItem(key) {
        if (sessionStorage)
            return sessionStorage.getItem(key);
        return Cookie.getItem(key);
    },
    /**
     * 移除存储值
     * @param {string} key 键
     */
    removeItem(key) {
        if (sessionStorage) {
            sessionStorage.removeItem(key);
        }
        else {
            Cookie.removeItem(key);
        }
    },
    /** 清除所有存储值 */
    clear() {
        if (sessionStorage) {
            sessionStorage.clear();
        }
        else {
            Cookie.clear();
        }
    }
};
/**
 * 获取url中的参数并组装为参数对象
 * @param {string} [url=window.location.href] 默认为当前window.location.href
 * @returns {object} 参数对象
 */
export function getUrlParamObj(url = window.location.href) {
    if (url.indexOf('?') < 0)
        return {};
    url = url.substring(url.indexOf('?') + 1);
    return convertStr2Obj(url);
}
/**
 * 将长字符串参数转化为对象
 * @param {string} paramStr 长字符串参数
 * @returns {IResult} 对象参数
 */
export function convertStr2Obj(paramStr) {
    const result = {};
    const kvs = paramStr.split('&');
    for (let i = 0, len = kvs.length; i < len; i++) {
        const item = kvs[i];
        const kv = item.split('=');
        result[kv[0]] = kv[1];
    }
    return result;
}
/**
 * 将参数对象转化为长字符串
 * @param {object} paramObj 参数对象
 * @returns {string} 长字符串
 */
export function convertObj2Str(paramObj) {
    const keys = Object.keys(paramObj);
    const result = [];
    for (let i = 0, len = keys.length; i < len; i++) {
        const key = keys[i];
        const value = paramObj[key];
        if (i < len - 1) {
            result.push(key + '=' + value + '&');
        }
        else {
            result.push(key + '=' + value);
        }
    }
    return result.join('');
}
/**
 * 为链接拼接参数
 * @param {string} [url=window.location.href] 默认为当前window.location.href
 * @param {object} paramObj 参数对象
 * @returns {string} 拼接好参数的链接
 */
export function urlJoinParams(url = window.location.href, paramObj) {
    if (!paramObj || JSON.stringify(paramObj) === '{}')
        return url;
    if (url.indexOf('?') === -1)
        return url + '?' + convertObj2Str(paramObj);
    const p = getUrlParamObj(url);
    for (const key in paramObj) {
        if (Object.prototype.hasOwnProperty.call(paramObj, key)) {
            p[key] = paramObj[key];
        }
    }
    url = url.split('?')[0];
    return url + '?' + convertObj2Str(p);
}
/**
 * 字符串分割处理
 * @param {string} target 目标字符串
 * @param {string} separator 分隔符
 * @returns {string[]} 分割好的数组
 */
export function separate(target, separator) {
    if (target) {
        if (target.indexOf(separator) === -1)
            return [target];
        return target.split(separator);
    }
    return [];
}
/**
 * 参数补零
 * @param {number} arg 参数
 * @param {string} [zero='0'] 补零参数
 * @returns {string} 处理好的参数
 */
export function addZeros(arg, zero = '0') {
    return arg < 10 ? zero + arg : arg + '';
}
/**
 * 获取与给定时间(默认当前时间)相差某天的时间
 * @param {number} [day=0] 相差的天数
 * @param {Date} [date=new Date()] 指定的date
 * @returns {Date} 处理好的时间
 */
export function getDeltaDate(day = 0, date = new Date()) {
    return new Date(date.setDate(date.getDate() + day));
}
/**
 * 获取最近几天的时间数据
 * @param {number} num 天数
 * @param {string} fmt 时间格式化默认 yyyy.MM.dd, 参照getFormatDateString
 */
export function getDateData(num, fmt = 'yyyy.MM.dd') {
    let date = new Date(), dateList = [];
    for (var i = 0; i < num; i++) {
        const str = i === 0 ? '今天' : i === 1 ? '昨天' : getFormatDateString('MMdd', date);
        dateList.push({ time: getFormatDateString(fmt, date), dateStr: str });
        date = getDeltaDate(-1, date);
    }
    return dateList;
}
/**
 * 将播放秒数转换为时分秒格式
 * @param {number} time 秒数
 * @returns {string} 处理好的格式
 */
export function convertPlayTime(time) {
    const hour = addZeros(Math.floor(time / 3600));
    const minute = addZeros(Math.floor(time % 3600 / 60));
    const second = addZeros(time % 3600 % 60);
    return '' + hour + ':' + minute + ':' + second;
}
/**
 * 将时间字符(hh:mm:ss)串转换为秒数
 * @param {string} paramStr 字符参数
 */
export function convertStr2Second(paramStr) {
    if (!/^(?:[01]\d|2[0-3])(?::[0-5]\d){2}$/.test(paramStr)) {
        console.error('the parameter does not conform to [hh:mm:ss].');
        return 0;
    }
    const list = paramStr.split(':');
    return parseInt(list[0], 10) * 3600 + parseInt(list[1], 10) * 60 + parseInt(list[2], 10);
}
/** 将日期字符串转换为毫秒时间戳 */
export function convertDateStr2Stamp(str) {
    if (!str)
        return Date.now();
    const date = new Date(str);
    return date.getTime() || 0;
}
/**
 * 将date时间转换为UTC时间戳
 * @param {Date} date 待转换时间
 * @returns {string} 处理好的时间戳
 */
export function convertDate2UTC(date) {
    const year = date.getFullYear();
    const month = addZeros(date.getMonth() + 1);
    const day = addZeros(date.getDate());
    const hour = addZeros(date.getHours() - 8);
    const min = addZeros(date.getMinutes());
    const sec = addZeros(date.getSeconds());
    return '' + year + month + day + 'T' + hour + min + sec + 'Z';
}
/**
 * 将UTC时间戳转换为date时间
 * @param {string} utcTime 待转换的UTC时间戳
 * @returns {Date} 处理好的date时间
 */
export function parseUTC2Date(utcTime) {
    if (!utcTime || utcTime.indexOf('Z') === -1)
        return new Date();
    let year = parseInt(utcTime.substr(0, 4), 10);
    let month = parseInt(utcTime.substr(4, 2), 10);
    let day = parseInt(utcTime.substr(6, 2), 10);
    let hour = parseInt(utcTime.substr(9, 2), 10);
    let min = parseInt(utcTime.substr(11, 2), 10);
    let sec = parseInt(utcTime.substr(13, 2), 10);
    // 处理parseInt('0X')等于零的问题
    if (month === 0)
        month = parseInt(utcTime.substr(5, 1), 10);
    if (day === 0)
        day = parseInt(utcTime.substr(7, 1), 10);
    if (hour === 0)
        hour = parseInt(utcTime.substr(10, 1), 10);
    if (min === 0)
        min = parseInt(utcTime.substr(12, 1), 10);
    if (sec === 0)
        sec = parseInt(utcTime.substr(14, 1), 10);
    return new Date(year, month - 1, day, hour + 8, min, sec);
}
/**
 * 获取格式化时间字符串
 * @param {string} [fmt='yyyy-MM-dd hh:mm:ss.SS Ww'] 格式化pattern 'yyyy-MM-dd hh:mm:ss.SS Ww' -> '年-月-日 时:分:秒.毫秒 周几上下午'
 * @param {Date} [date=new Date()] 指定的date
 * @returns {string} 格式化的时间字符串
 * @example
 * let now = getFormatDateString('yyyy-MM-dd hh:mm:ss.SS Ww');// 当前时间格式化字符串
 * let date = new Date('2016/09/12 12:00:00');
 * let dateString = getFormatDateString('yyyy-MM-dd hh:mm:ss.SS Ww', date);// date时间格式化字符串
 */
export function getFormatDateString(fmt = 'yyyy-MM-dd hh:mm:ss.SS Ww', date = new Date()) {
    const weekday = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
    const apm = ['上午', '中午', '下午'];
    const o = {
        'M+': date.getMonth() + 1,
        'd+': date.getDate(),
        'h+': date.getHours(),
        'm+': date.getMinutes(),
        's+': date.getSeconds(),
        'w+': date.getHours() > 12 ? apm[2] : (date.getHours() === 12 ? apm[1] : apm[0]),
        'W+': weekday[date.getDay()],
        'S+': date.getMilliseconds()
    };
    if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
    }
    for (const k in o) {
        if (new RegExp('(' + k + ')').test(fmt)) {
            fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? '' + o[k] : ('00' + o[k]).substr(('' + o[k]).length));
        }
    }
    return fmt;
}
/**
 * ajax配置定义
 * @typedef IAjax
 * @property {'GET' | 'POST'} [type='GET'] 请求类型
 * @property {string} url 请求地址
 * @property {boolean} [json=false] 参数是否为json字符串
 * @property {object} [params] 请求参数对象
 * @property {number} [timeout] 请求超时毫秒数
 * @property {function} [success] 请求成功的回调
 * @property {function} [error] 请求失败的回调
 */
/**
 * 通用ajax请求
 * @param {IAjax} ajax 请求参数
 * @returns {XMLHttpRequest} xhm对象
 */
export function request(ajax) {
    if (!ajax.type)
        ajax.type = 'GET';
    const xhm = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    let t = 0; // 定时器id
    xhm.onreadystatechange = () => {
        if (xhm.readyState === 4) {
            if (t)
                clearTimeout(t);
            if (xhm.status === 200) { // 成功回调
                if (ajax.success)
                    ajax.success(xhm.response);
            }
            if (xhm.status === 404) {
                if (ajax.error)
                    ajax.error('accessed error 404');
            }
            if (xhm.status === 500) {
                if (ajax.error)
                    ajax.error('accessed error 500');
            }
        }
    };
    xhm.onerror = (e) => {
        if (t)
            clearTimeout(t);
        ajax.error && ajax.error(e);
    };
    if (ajax.timeout && typeof ajax.timeout === 'number') { // 配置了超时
        t = setTimeout(() => {
            xhm.abort();
            clearTimeout(t);
            ajax.error && ajax.error('timeout');
            throw new Error('Request Timeout And Aborted!');
        }, ajax.timeout);
    }
    const requestType = ajax.type.toUpperCase();
    if (requestType === 'GET') {
        xhm.open(requestType, urlJoinParams(ajax.url, ajax.params || {}), true);
        xhm.send(null);
    }
    else {
        xhm.open(requestType, ajax.url, true);
        if (ajax.headers && typeof ajax.headers === 'object') {
            for (const key in ajax.headers) {
                if (Object.prototype.hasOwnProperty.call(ajax.headers, key)) {
                    const value = ajax.headers[key];
                    xhm.setRequestHeader(key, value);
                }
            }
        }
        else {
            xhm.setRequestHeader('Content-Type', 'application/json;charset=utf-8');
        }
        xhm.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        if (ajax.params) {
            if (ajax.json) {
                xhm.send(JSON.stringify(ajax.params || {}));
            }
            else {
                xhm.send(convertObj2Str(ajax.params || {}));
            }
        }
        else {
            xhm.send(null);
        }
    }
    return xhm;
}
/**
 * 获取数组中某一页的数据
 * @param {T[]} data 数据数组
 * @param {number} page 页数
 * @param {number} size 一页条数
 */
export function pageData(data, page, size) {
    if (!data || data.length < 1)
        return [];
    return data.filter((d, i) => i >= (page - 1) * size && i < page * size);
}
/**
 * 从数组随机获取N个数据
 * @param {T[]} data 数据数组
 * @param {number} [count] 获取数据个数
 */
export function randomData(data, count = 0) {
    if (!data || data.length < 1)
        return [];
    if (count > data.length || count < 1)
        count = data.length;
    const dataCopy = data.slice();
    const result = [];
    while (result.length < count) {
        for (let i = 0, len = dataCopy.length; i < len; i++) {
            const index = Math.floor(Math.random() * len);
            result.push(dataCopy[index]);
            dataCopy.splice(index, 1);
            break;
        }
    }
    return result;
}
/**
 * 裁剪数组并占位填充
 * @param {*} data 数组数据
 * @param {number} [start] 开始索引
 * @param {number} [end] 结束索引
 */
export function sliceData(data, start, end) {
    if (Object.prototype.toString.call(data) !== '[object Array]')
        data = [];
    const total = data.length;
    if (!start)
        start = 0;
    if (!end)
        end = total;
    const value = end - start;
    if (value < 0) {
        throw new Error('[end] value should greater than [start] value');
    }
    const result = data.slice(start, end);
    if (result.length < value) {
        const count = value - result.length;
        for (let i = 0; i < count; i++) {
            result.push('');
        }
    }
    return result;
}
/**
 * 深拷贝
 * @param {*} target 目标对象
 */
export function clone(target) {
    if (target === null)
        return target;
    if (typeof target !== 'object')
        return target;
    if (target.constructor === Date)
        return new Date(target.getTime());
    const newObj = new target.constructor();
    for (const key in target) {
        if (target.hasOwnProperty(key)) {
            const val = target[key];
            newObj[key] = typeof val === 'object' ? clone(val) : val;
        }
    }
    return newObj;
}
/**
 * 扩展对象
 * @param {IResult} target 目标对象
 * @param {IResult} [obj] 扩展对象参数
 */
export function assign(target, obj) {
    if (!target)
        return obj;
    if (!obj)
        return target;
    const copy = clone(target), copyObj = clone(obj);
    for (const key in copyObj) {
        if (obj.hasOwnProperty(key))
            copy[key] = copyObj[key];
    }
    return copy;
}
/**
 * 裁剪文字
 * @param {string} text 文字内容
 * @param {number} length 保留文字个数
 * @param {boolean} [ellipsis] 是否添加省略号
 */
export function cropText(text, length, ellipsis) {
    if (typeof text !== 'string')
        return '';
    let str = "", len = 0, char;
    for (let i = 0; char = text.charAt(i); i++) {
        len += (char.match(/[^\x00-\xff]/) != null ? 2 : 1);
        if (char == "W" || char == "w")
            len = len + 1;
        if (len > length)
            break;
        str += char;
    }
    if (!ellipsis || str == "" || len <= length)
        return str;
    return str + "...";
}
/**
 * 设置数据轮播
 * @param {any[]} data 轮播数据
 * @param {IBannerConfig} config 轮播配置
 */
export function setBanner(data, config) {
    let timer = 0;
    let index = config?.index || 0;
    const staticMode = config?.static;
    const reverse = config?.reverse;
    const len = data?.length;
    /** 开始 */
    const start = (dir = 1) => {
        let prev = reverse ? index + dir : index - dir;
        if (prev < 0)
            prev = len - 1;
        if (prev > len - 1)
            prev = 0;
        config?.change?.(data?.[index], index, prev < 0 ? 0 : prev);
        if (len < 2)
            return;
        const lay = () => {
            if (reverse) {
                index = index > 0 ? --index : len - 1;
            }
            else {
                index = index < len - 1 ? ++index : 0;
            }
        };
        if (staticMode)
            lay();
        else {
            stop();
            timer = setTimeout(() => {
                lay(), start(dir);
            }, config?.interval || 5000);
        }
    };
    /** 停止 */
    const stop = () => { if (timer)
        clearTimeout(timer); };
    /** 上一个 */
    const prev = () => {
        stop(), index = currentIndex();
        index--;
        index = index < 0 ? len - 1 : index;
        start(-1);
    };
    /** 下一个 */
    const next = () => {
        stop(), index = currentIndex();
        index++;
        index = index > len - 1 ? 0 : index;
        start(1);
    };
    /** 当前数据 */
    const current = () => data?.[index];
    /** 当前索引 */
    const currentIndex = () => index;
    return { start, stop, prev, next, current, currentIndex };
}
/**
 * 历史栈
 * @namespace History
 */
export const History = {
    /**
     * 记录历史
     * @param {string} url 跳转链接
     * @param {*} [data] 要记录的数据
     * @param {string} [returnUrl] 返回地址默认window.location.href
     * @param {boolean} [inject] 不注入returnUrl
     * @param {string} [key] 唯一键默认window.location.pathname
     */
    pushState(url, data, returnUrl = window.location.href, inject, key = window.location.pathname) {
        if (url) {
            if (data)
                Storage.setItem(key, JSON.stringify(data)); // 保存当前页面数据
            if (!inject) {
                if (returnUrl.indexOf('?') !== -1)
                    returnUrl = returnUrl.split('?')[0];
                let target = url;
                if (target.indexOf('?') !== -1)
                    target = target.split('?')[0]; // 去除参数
                const host = window.location.host;
                if (target.indexOf(host) !== -1)
                    target = target.split(host)[1]; // 截取前缀
                if (target)
                    Storage.setItem(target, JSON.stringify({ returnUrl })); // 保存返回地址
            }
            window.location.href = url;
        }
    },
    /**
     * 读取历史栈顶的链接
     * @param {boolean} [keep] 是否保留当前记录
     * @param {string} [key] 唯一键默认window.location.pathname
     */
    getState(keep, key = window.location.pathname) {
        const state = Storage.getItem(key) || '';
        try {
            const data = JSON.parse(state);
            !keep && Storage.removeItem(key);
            return data;
        }
        catch (e) {
            return { returnUrl: '' };
        }
    },
    /** 清空历史栈 */
    clearState() {
        Storage.clear();
    }
};
/**
 * 获取并设置默认值
 * @param {*} arg 参数
 * @param {*} defaultValue 默认值
 */
export function defaultGet(arg, defaultValue) {
    if (isNaN(arg))
        return defaultValue;
    if (typeof arg === 'number')
        return arg;
    return !!arg ? arg : defaultValue;
}
/**
 * 倒计时
 * @param {number} seconds 倒计时秒数
 * @param {Function} end 倒计时结束回调
 * @param {Function} [run] 每秒回调
 */
export function countDown(seconds, end, run) {
    if (typeof seconds !== 'number')
        throw new TypeError('[seconds] must be a number.');
    let count = Math.floor(seconds), t = 0;
    (function handler() {
        run && run(count);
        t = setTimeout(() => {
            clearTimeout(t);
            --count < 0 ? end && end() : handler();
        }, 1000);
    })();
    return { stop: () => { clearTimeout(t), t = 0; } };
}
/**
 * 数组数据按给定索引依次排序
 * @param {Array<T>} data 数据数组
 * @param {number} index 定位索引
 */
export function sortIndexData(data, index) {
    const h = [], t = [];
    for (let i = 0; i < data.length; i++) {
        if (i < index) {
            h.push(data[i]);
        }
        else {
            t.push(data[i]);
        }
    }
    return t.concat(h);
}
/**
 * 数字切换动画
 * @param {HTMLElement} dom 指定dom元素
 * @param {number} from 指定开始数字
 * @param {number} to 指定结束数字
 * @param {number} speed 速度
 * @param {string} template html模板 如: <span>#number分</span>
 */
export function numberAnimate(dom, from, to, speed = 300, template) {
    const interval = Math.floor(speed / Math.abs(from - to));
    (function render() {
        const t = setTimeout(() => {
            clearTimeout(t);
            if (to > from) {
                dom.innerHTML = template ? template.replace(/#number/g, ++from + '') : ++from + '';
            }
            else if (to < from) {
                dom.innerHTML = template ? template.replace(/#number/g, --from + '') : --from + '';
            }
            else
                return;
            render();
        }, interval);
    })();
}
/**
 * 循环执行
 * @param {Function} run 每次执行的回调
 * @param {number} [interval] 执行间隔,默认1000
 */
export function loop(run, interval = 1000) {
    if (typeof interval !== 'number')
        throw new TypeError('[interval] must be a number.');
    let t = 0;
    (function handler() {
        run && run();
        t = setTimeout(() => {
            clearTimeout(t);
            handler();
        }, interval);
    })();
    return { stop: () => { clearTimeout(t), t = 0; } };
}
